package com.molichuxing.services.business.service.impl;

import com.molichuxing.framework.dto.Paged;
import com.molichuxing.framework.exception.BizException;
import com.molichuxing.framework.utils.DateUtil;
import com.molichuxing.framework.utils.ObjectCastUtil;
import com.molichuxing.framework.utils.SnowflakeUtil;
import com.molichuxing.services.business.dto.request.create.InvoiceBizCreateDto;
import com.molichuxing.services.business.dto.response.InvoiceInfoBizDto;
import com.molichuxing.services.business.service.InvoiceBizService;
import com.molichuxing.services.infrastructure.convertor.Convert;
import com.molichuxing.services.infrastructure.dto.request.create.InvoiceApplyCreateDto;
import com.molichuxing.services.infrastructure.dto.request.create.InvoiceInfoCreateDto;
import com.molichuxing.services.infrastructure.dto.request.modify.InvoiceDto;
import com.molichuxing.services.infrastructure.dto.response.InvoiceApplyDto;
import com.molichuxing.services.infrastructure.dto.response.InvoiceDetailDto;
import com.molichuxing.services.infrastructure.dto.response.InvoiceInfoDto;
import com.molichuxing.services.infrastructure.service.InvoiceApplyService;
import com.molichuxing.services.infrastructure.service.InvoiceDetailService;
import com.molichuxing.services.infrastructure.service.InvoiceInfoService;
import com.molichuxing.services.infrastructure.service.InvoiceService;
import com.molichuxing.services.property.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 发票申请 服务实现类
 * </p>
 *
 * @author zhaopenghui
 * @since 2019-09-11
 */
@Service("invoiceBizService")
public class InvoiceBizServiceImpl implements InvoiceBizService {

    @Autowired
    InvoiceApplyService invoiceApplyService;

    @Autowired
    InvoiceInfoService invoiceInfoService;

    @Autowired
    InvoiceDetailService invoiceDetailService;

    @Autowired
    InvoiceService invoiceService;

    @Value("${service.sell.Name}")
    private String SERVICE_SELL_NAME;
    @Value("${service.sell.tax.Number}")
    private String SERVICE_SELL_TAX_NUMBER;
    @Value("${invoice.rent}")
    private String INVOICE_RENT;
    @Value("${invoice.month.unit}")
    private String INVOICE_MONTH_UNIT;


    /**
     * 获取发票列表
     *
     * @param map      key:status Integer 发票状态
     *                 key:orderCode Long 订单编号
     *                 key:userId Integer 用户id
     *                 key：businessType Integer 业务类型
     *                 key:startApplyTime LocalDateTime 申请开始时间
     *                 key:endApplyTime LocalDateTime 申请结束时间
     * @param pageNum
     * @param pageSize
     * @return
     * @throws Exception
     */
    @Override
    public Paged<InvoiceInfoBizDto> getInvoiceInfoPage(Map<String, Object> map, Integer pageNum, Integer pageSize) throws Exception {
        modifyInvoiceInfoMap(map);
        Paged<InvoiceInfoDto> invoiceInfoPage = invoiceInfoService.getInvoiceInfoPage(map, pageNum, pageSize);

        if (null == invoiceInfoPage) {
            return null;
        }
        List<InvoiceInfoDto> invoiceInfoList = invoiceInfoPage.getList();
        if (null == invoiceInfoList || invoiceInfoList.size() == 0) {
            return null;
        }

        List<Integer> applyIdList = new ArrayList<>();
        List<String> serialNumberList = new ArrayList<>();
        for (InvoiceInfoDto invoiceInfoDto : invoiceInfoList) {
            applyIdList.add(invoiceInfoDto.getInvoiceApplyId());
            serialNumberList.add(invoiceInfoDto.getInvoiceSerialNumber());
        }
        Map<Integer, InvoiceApplyDto> invoiceApplyMap = invoiceApplyService.getInvoiceApplyMap(applyIdList);
        Map<String, InvoiceDetailDto> invoiceDetailMap = invoiceDetailService.getInvoiceDetailMapBySerialNumber(serialNumberList);
        List<InvoiceInfoBizDto> pagedReturnList = getPagedReturnList(invoiceInfoList, invoiceApplyMap, invoiceDetailMap);
        return new Paged<InvoiceInfoBizDto>(pagedReturnList, invoiceInfoPage.getTotal(), pageSize);
    }


    /**
     * 发票列表修改map内容
     *
     * @param map
     * @throws Exception
     */
    private void modifyInvoiceInfoMap(Map<String, Object> map) throws Exception {
        Object orderCodeObj = map.get("orderCode");
        Object userIdObj = map.get("userId");
        Object invoiceTitleTypeObj = map.get("invoiceTitleType");
        if (orderCodeObj != null || userIdObj != null || invoiceTitleTypeObj != null) {
            Long orderCode = ObjectCastUtil.castLongPackType(orderCodeObj);
            Integer userId = ObjectCastUtil.castInteger(userIdObj);
            Integer invoiceTitleType = ObjectCastUtil.castInteger(invoiceTitleTypeObj);

            List<Integer> invoiceApplyIds = invoiceApplyService.getInvoiceApplyIds(orderCode, userId,invoiceTitleType);
            if (null == invoiceApplyIds || invoiceApplyIds.size() < 1) {
                invoiceApplyIds = new ArrayList<>();
                invoiceApplyIds.add(0);
            }
            map.put("invoiceApplyIdList", invoiceApplyIds);
        }
    }

    /**
     * 获取分页返回集合
     *
     * @param invoiceInfoList
     * @param invoiceApplyMap
     * @return
     * @throws Exception
     */
    private List<InvoiceInfoBizDto> getPagedReturnList(List<InvoiceInfoDto> invoiceInfoList,
                                                       Map<Integer, InvoiceApplyDto> invoiceApplyMap,
                                                       Map<String, InvoiceDetailDto> invoiceDetailMap) throws Exception {
        List<InvoiceInfoBizDto> invoiceInfoBizDtoList = Convert.toInvoiceInfoBizDtoList(invoiceInfoList);
        for (InvoiceInfoBizDto invoiceInfoBizDto : invoiceInfoBizDtoList) {
            Integer invoiceApplyId = invoiceInfoBizDto.getInvoiceApplyId();
            String invoiceSerialNumber = invoiceInfoBizDto.getInvoiceSerialNumber();
            if (invoiceApplyMap != null) {
                InvoiceApplyDto invoiceApplyDto = invoiceApplyMap.get(invoiceApplyId);
                Convert.toInvoiceInfoBizDto(invoiceApplyDto, invoiceInfoBizDto);
            }
            if (invoiceDetailMap != null) {
                InvoiceDetailDto invoiceDetailDto = invoiceDetailMap.get(invoiceSerialNumber);
                Convert.toInvoiceInfoBizDto(invoiceDetailDto, invoiceInfoBizDto);
            }

        }
        return invoiceInfoBizDtoList;
    }

    /**
     * 发票添加
     *
     * @param invoiceBizCreateList
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
    public Boolean createInvoice(List<InvoiceBizCreateDto> invoiceBizCreateList) throws Exception {
        if (null == invoiceBizCreateList || invoiceBizCreateList.size() < 1) {
            throw new BizException("申请开票失败");
        }

        List<Integer> invoiceIdList = new ArrayList<>();
        for (InvoiceBizCreateDto invoiceBizCreateDto : invoiceBizCreateList) {
            Integer invoiceId = invoiceBizCreateDto.getInvoiceId();
            invoiceIdList.add(invoiceId);
        }
        Map<Integer, InvoiceDto> invoiceMap = invoiceService.getInvoiceMap(invoiceIdList);
        if (null == invoiceMap || invoiceMap.size() < 1) {
            throw new BizException("申请开票失败");
        }

        for (InvoiceBizCreateDto invoiceBizCreateDto : invoiceBizCreateList) {
            createInvoiceApply(invoiceBizCreateDto, invoiceMap);
        }
        return true;
    }

    /**
     * 添加发票申请
     *
     * @param invoiceBizCreateDto
     * @param invoiceMap
     * @return
     * @throws Exception
     */
    private Boolean createInvoiceApply(InvoiceBizCreateDto invoiceBizCreateDto, Map<Integer, InvoiceDto> invoiceMap) throws Exception {
        LocalDateTime nowTime = DateUtil.convertDateToLDT(new Date());
        Integer invoiceId = invoiceBizCreateDto.getInvoiceId();
        InvoiceDto invoiceDto = null;
        if (invoiceId != null && invoiceMap != null) {
            invoiceDto = invoiceMap.get(invoiceId);
        }
        if (null == invoiceDto) {
            throw new BizException("申请开票失败");
        }

        InvoiceApplyCreateDto invoiceApplyCreateDto = Convert.toInvoiceApplyCreateDto(invoiceBizCreateDto, invoiceDto);
        invoiceApplyCreateDto.setApplyTime(nowTime);
        Integer invoiceApplyId = invoiceApplyService.createInvoiceApply(invoiceApplyCreateDto);
        createInvoiceInfo(invoiceBizCreateDto, invoiceApplyId, invoiceDto, nowTime);
        return true;
    }

    /**
     * 添加发票信息
     *
     * @param invoiceBizCreateDto
     * @param invoiceApplyId
     * @param invoiceDto
     * @param nowTime
     * @return
     * @throws Exception
     */
    private Boolean createInvoiceInfo(InvoiceBizCreateDto invoiceBizCreateDto, Integer invoiceApplyId,
                                      InvoiceDto invoiceDto, LocalDateTime nowTime) throws Exception {

        Integer businessType = invoiceDto.getBusinessType();
        Integer type = invoiceDto.getType();
        String typeStr = invoiceDto.getTypeStr();
        BigDecimal amount = invoiceDto.getAmount();
        BigDecimal serviceFee = invoiceDto.getServiceFee();
        Integer currentPeriods = invoiceDto.getCurrentPeriods();
        Integer totalPeriods = invoiceDto.getTotalPeriods();
        Integer transportTime = invoiceDto.getTransportTime();

        List<InvoiceInfoCreateDto> invoiceInfoCreateDtoList = Arrays.asList(new InvoiceInfoCreateDto(), new InvoiceInfoCreateDto());
        for (int i = 0; i < invoiceInfoCreateDtoList.size(); i++) {
            InvoiceInfoCreateDto invoiceInfoCreateDto = invoiceInfoCreateDtoList.get(i);
            BeanUtils.copyProperties(invoiceBizCreateDto, invoiceInfoCreateDto);
            invoiceInfoCreateDto.setInvoiceApplyId(invoiceApplyId);
            invoiceInfoCreateDto.setInvoiceSerialNumber(SnowflakeUtil.getId());
            invoiceInfoCreateDto.setStatus(InvoiceApplyStatusEnum.MAKE_OUT.getValue());
            invoiceInfoCreateDto.setApplyTime(nowTime);
            invoiceInfoCreateDto.setBusinessType(businessType);

            Boolean oneInvoiceBoolean = InvoiceBusinessTypeEnum.SUBSCRIBE.getValue() == businessType
                    && (InvoiceTypeEnum.ADD_MILEAGE.getValue() == type
                    || InvoiceTypeEnum.TIME_OUT.getValue() == type
                    || InvoiceTypeEnum.BEYOND_MILEAGE.getValue() == type
                    || InvoiceTypeEnum.QUALITY_LOSS.getValue() == type);
            StringBuilder description = new StringBuilder();

            if (oneInvoiceBoolean) {
                if (InvoiceTypeEnum.QUALITY_LOSS.getValue() == type) {
                    invoiceInfoCreateDto.setInvoiceContentType(InvoiceContentTypeEnum.SERVICE_FEE.getValue());
                    invoiceInfoCreateDto.setInvoiceContent(InvoiceContentEnum.MODERN_SERVICE_CHARGE.getName());
                    description.append(typeStr).append("-").append(InvoiceContentTypeEnum.SERVICE_FEE.getName());
                } else {
                    invoiceInfoCreateDto.setInvoiceContentType(InvoiceContentTypeEnum.RENT.getValue());
                    invoiceInfoCreateDto.setInvoiceContent(InvoiceContentEnum.CART_FEE.getName());
                    description.append(typeStr).append("-").append(INVOICE_RENT);
                }
                invoiceInfoCreateDto.setInvoiceAmount(amount);
                invoiceInfoCreateDto.setDescription(String.valueOf(description));
                break;
            } else {
                if (i == 0) {
                    invoiceInfoCreateDto.setInvoiceContentType(InvoiceContentTypeEnum.RENT.getValue());
                    invoiceInfoCreateDto.setInvoiceContent(InvoiceContentEnum.CART_FEE.getName());
                    invoiceInfoCreateDto.setInvoiceAmount(amount.subtract(serviceFee));
                } else {
                    invoiceInfoCreateDto.setInvoiceContentType(InvoiceContentTypeEnum.SERVICE_FEE.getValue());
                    invoiceInfoCreateDto.setInvoiceContent(InvoiceContentEnum.PLATFORM_SERVICE_CHARGE.getName());
                    invoiceInfoCreateDto.setInvoiceAmount(serviceFee);
                    invoiceInfoCreateDto.setSellName(SERVICE_SELL_NAME);
                    invoiceInfoCreateDto.setSellTaxNumber(SERVICE_SELL_TAX_NUMBER);
                    invoiceInfoCreateDto.setSellAddress("");
                    invoiceInfoCreateDto.setSellPhone("");
                    invoiceInfoCreateDto.setSellBank("");
                    invoiceInfoCreateDto.setSellBankNumber("");
                }

                if (InvoiceBusinessTypeEnum.EXCLUSIVEL.getValue() == businessType && i == 0) {
                    description.append(currentPeriods).append("/").append(totalPeriods).append("-").append(INVOICE_RENT);
                }
                if (InvoiceBusinessTypeEnum.EXCLUSIVEL.getValue() == businessType && i == 1) {
                    description.append(currentPeriods).append("/").append(totalPeriods).append("-").append(InvoiceContentTypeEnum.SERVICE_FEE.getName());
                }
                if (InvoiceBusinessTypeEnum.SUBSCRIBE.getValue() == businessType && i == 0) {
                    description.append(transportTime).append(INVOICE_MONTH_UNIT).append("-").append(INVOICE_RENT);
                }
                if (InvoiceBusinessTypeEnum.SUBSCRIBE.getValue() == businessType && i == 1) {
                    description.append(transportTime).append(INVOICE_MONTH_UNIT).append("-").append(InvoiceContentTypeEnum.SERVICE_FEE.getName());
                }

                invoiceInfoCreateDto.setDescription(String.valueOf(description));
            }
        }
        return invoiceInfoService.createInvoiceInfoBatch(invoiceInfoCreateDtoList);
    }


}
